﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/9 13:51:05
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Java;
using ImageK.Util;

namespace ImageK.Process
{
	/**
This is an 8-bit image and methods that operate on that image. Based on the ImageProcessor class
from "KickAss Java Programming" by Tonny Espeset.
*/
	public class ByteProcessor : ImageProcessor
	{

	    const int ERODE = 10, DILATE = 11;
	    protected byte[] pixels;
	    protected byte[] snapshotPixels;
	    private int bgColor = 255; //white
	    private bool bgColorSet;
	    private int min = 0, max = 255;
	    private int binaryCount, binaryBackground;

	    /**Creates a ByteProcessor from an AWT Image. */
	    public ByteProcessor(Image img)
	    {
		    width = img.Width;
		    height = img.Height;
		    resetRoi();
		    pixels = new byte[width * height];
            throw new NotImplementedException();
            // PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, false);
            // try
            // {
            //  pg.grabPixels();
            // }
            // catch (InterruptedException e)
            // {
            //  System.err.println(e);
            // };
            // cm = pg.getColorModel();
            // if (cm is IndexColorModel)
            //  pixels = (byte[])(pg.getPixels());
            // if ((cm is IndexColorModel) && ((IndexColorModel)cm).getTransparentPixel() != -1) {
            //  IndexColorModel icm = (IndexColorModel)cm;
            //  int mapSize = icm.getMapSize();
            //  byte[] reds = new byte[mapSize];
            //  byte[] greens = new byte[mapSize];
            //  byte[] blues = new byte[mapSize];
            //  icm.getReds(reds);
            //  icm.getGreens(greens);
            //  icm.getBlues(blues);
            //  cm = new IndexColorModel(8, mapSize, reds, greens, blues);
            // }
        }

	    /**Creates a blank ByteProcessor of the specified dimensions. */
	    public ByteProcessor(int width, int height): this(width, height, new byte[width * height], null)
		{
	    }

	    /**Creates a ByteProcessor from a byte array. */
	    public ByteProcessor(int width, int height, byte[] pixels): this(width, height, pixels, null)
		{
	    }

	    /**Creates a ByteProcessor from a pixel array and IndexColorModel. */
	    public ByteProcessor(int width, int height, byte[] pixels, ColorModel cm)
	    {
		    if (pixels != null && width * height != pixels.Length)
			    throw new ArgumentException(WRONG_LENGTH);
		    this.width = width;
		    this.height = height;
		    resetRoi();
		    this.pixels = pixels;
		    this.cm = cm;
	    }

	    /** Creates a ByteProcessor from a TYPE_BYTE_GRAY BufferedImage. */
	    public ByteProcessor(Bitmap bi)
        {
            throw new NotImplementedException();
            //       if (bi.getType() != BufferedImage.TYPE_BYTE_GRAY)
            //  throw new ArgumentException("Type!=TYPE_BYTE_GRAYY");
            // WritableRaster raster = bi.getRaster();
            // DataBuffer buffer = raster.getDataBuffer();
            // pixels = ((DataBufferByte)buffer).getData();
            // width = raster.getWidth();
            // height = raster.getHeight();
            // resetRoi();
        }

	    /** Creates a ByteProcessor from an ImageProcessor. 16-bit and 32-bit
	     * pixel data are scaled from min-max to 0-255 if 'scale' is true.
	     * @see ImageProcessor#convertToByteProcessor
	     * @see ImageProcessor#convertToShortProcessor
	     * @see ImageProcessor#convertToFloatProcessor
	     * @see ImageProcessor#convertToColorProcessor
	    */
	    public ByteProcessor(ImageProcessor ip, bool scale)
	    {
		    ImageProcessor bp;
		    if (ip is ByteProcessor)
			    bp = ip.duplicate();
		    else
			    bp = ip.convertToByte(scale);
		    this.width = bp.getWidth();
		    this.height = bp.getHeight();
		    resetRoi();
		    this.pixels = (byte[])bp.getPixels();
		    this.cm = bp.getCurrentColorModel();
	    }

	    public override Image createImage()
	    {
		    if (cm == null)
			    cm = getDefaultColorModel();
            return createBufferedImage();
	    }

        Image createBufferedImage()
	    {
		    if (raster == null)
		    {
			    // SampleModel sm = getIndexSampleModel();
			    // DataBuffer db = new DataBufferByte(pixels, width * height, 0);
			    // raster = Raster.createWritableRaster(sm, db, null);
		    }
		    // if (image == null || cm != cm2)
		    {
			    if (cm == null) cm = getDefaultColorModel();
			    // image = new BufferedImage(cm, raster, false, null);
                image = ImageUtil.BuildByBytes(pixels, width, height, cm);
			    cm2 = cm;
		    }
		    return image;
	    }

	    /** Returns this image as a BufferedImage. */
	    // public BufferedImage getBufferedImage()
	    // {
		   //  if (isDefaultLut())
		   //  {
			  //   BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
			  //   Graphics g = bi.createGraphics();
			  //   g.drawImage(createImage(), 0, 0, null);
			  //   return bi;
		   //  }
		   //  else
			  //   return (BufferedImage)createBufferedImage();
	    // }

	    /** Returns a new, blank ByteProcessor with the specified width and height. */
	    public override ImageProcessor createProcessor(int width, int height)
	    {
		    ImageProcessor ip2;
		    ip2 = new ByteProcessor(width, height, new byte[width * height], getColorModel());
		    if (baseCM != null)
			    ip2.setMinAndMax(min, max);
		    ip2.setInterpolationMethod(interpolationMethod);
		    return ip2;
	    }

	    public override ImageProcessor crop()
	    {
		    ImageProcessor ip2 = createProcessor(roiWidth, roiHeight);
		    byte[] pixels2 = (byte[])ip2.getPixels();
		    for (int ys = roiY; ys < roiY + roiHeight; ys++)
		    {
			    int offset1 = (ys - roiY) * roiWidth;
			    int offset2 = ys * width + roiX;
			    for (int xs = 0; xs < roiWidth; xs++)
				    pixels2[offset1++] = pixels[offset2++];
		    }
		    return ip2;
	    }

	    /** Returns a duplicate of this image. */
	    public override ImageProcessor duplicate()
	    {
		    ImageProcessor ip2 = createProcessor(width, height);
		    byte[] pixels2 = (byte[])ip2.getPixels();
		    Array.Copy(pixels, 0, pixels2, 0, width * height);
		    return ip2;
	    }

	    /**Make a snapshot of the current image.*/
	    public override void snapshot()
	    {
		    snapshotWidth = width;
		    snapshotHeight = height;
		    if (snapshotPixels == null || (snapshotPixels != null && snapshotPixels.Length != pixels.Length))
			    snapshotPixels = new byte[width * height];
            Array.Copy(pixels, 0, snapshotPixels, 0, width * height);
	    }

	    /** Reset the image from snapshot.*/
	    public override void reset()
	    {
		    if (snapshotPixels != null)
                Array.Copy(snapshotPixels, 0, pixels, 0, width * height);
	    }

	    /** Swaps the pixel and snapshot (undo) arrays. */
	    public override void swapPixelArrays()
	    {
		    if (snapshotPixels == null) return;
		    byte pixel;
		    for (int i = 0; i < pixels.Length; i++)
		    {
			    pixel = pixels[i];
			    pixels[i] = snapshotPixels[i];
			    snapshotPixels[i] = pixel;
		    }
	    }

	    /** Restore pixels that are within roi but not part of mask. */
	    public override void reset(ImageProcessor mask)
	    {
		    if (mask == null || snapshotPixels == null)
			    return;
		    if (mask.getWidth() != roiWidth || mask.getHeight() != roiHeight)
			    throw new ArgumentException(maskSizeError(mask));
		    byte[] mpixels = (byte[])mask.getPixels();
		    for (int y = roiY, my = 0; y < (roiY + roiHeight); y++, my++)
		    {
			    int i = y * width + roiX;
			    int mi = my * roiWidth;
			    for (int x = roiX; x < (roiX + roiWidth); x++)
			    {
				    if (mpixels[mi++] == 0)
					    pixels[i] = snapshotPixels[i];
				    i++;
			    }
		    }
	    }

	    public override void setSnapshotPixels(object pixels)
	    {
		    snapshotPixels = (byte[])pixels;
		    snapshotWidth = width;
		    snapshotHeight = height;
	    }

	    public override object getSnapshotPixels()
	    {
		    return snapshotPixels;
	    }

	    /** Sets pixels that are within roi and part of the mask to the foreground
		    color. Does nothing if the mask is not the same size as the ROI. */
	    public override void fill(ImageProcessor mask)
	    {
		    if (mask == null)
		    { fill(); return; }
		    int roiWidth = this.roiWidth, roiHeight = this.roiHeight;
		    int roiX = this.roiX, roiY = this.roiY;
		    if (mask.getWidth() != roiWidth || mask.getHeight() != roiHeight)
		    {
			    mask = getMask();
			    if (mask == null || mask.getWidth() != roiWidth || mask.getHeight() != roiHeight)
				    return;
		    }
		    byte[] mpixels = (byte[])mask.getPixels();
		    for (int y = roiY, my = 0; y < (roiY + roiHeight); y++, my++)
		    {
			    int i = y * width + roiX;
			    int mi = my * roiWidth;
			    for (int x = roiX; x < (roiX + roiWidth); x++)
			    {
				    if (mpixels[mi++] != 0)
					    pixels[i] = (byte)fgColor;
				    i++;
			    }
		    }
	    }

	    public override int getPixel(int x, int y)
	    {
		    if (x >= 0 && x < width && y >= 0 && y < height)
			    return pixels[y * width + x] & 0xff;
		    else
			    return 0;
	    }

	    public override int get(int x, int y)
	    {
		    return pixels[y * width + x] & 0xff;
	    }

	    public override void set(int x, int y, int value)
	    {
		    pixels[y * width + x] = (byte)value;
	    }

	    public override int get(int index)
	    {
		    return pixels[index] & 0xff;
	    }

	    public override void set(int index, int value)
	    {
		    pixels[index] = (byte)value;
	    }

	    public override float getf(int x, int y)
	    {
		    return pixels[y * width + x] & 0xff;
	    }

	    public override void setf(int x, int y, float value)
	    {
		    pixels[y * width + x] = (byte)(value + 0.5f);
	    }

	    public override float getf(int index)
	    {
		    return pixels[index] & 0xff;
	    }

	    public override void setf(int index, float value)
	    {
		    pixels[index] = (byte)value;
	    }

	    static double oldx, oldy;

	    /** Uses the current interpolation method (BILINEAR or BICUBIC) 
		    to calculate the pixel value at real coordinates (x,y). */
	    public override double getInterpolatedPixel(double x, double y)
	    {
		    if (interpolationMethod == BICUBIC)
			    return getBicubicInterpolatedPixel(x, y, this);
		    else
		    {
			    if (x < 0.0) x = 0.0;
			    if (x >= width - 1.0) x = width - 1.001;
			    if (y < 0.0) y = 0.0;
			    if (y >= height - 1.0) y = height - 1.001;
			    return getInterpolatedPixel(x, y, pixels);
		    }
	    }

	    public override int getPixelInterpolated(double x, double y)
	    {
		    if (interpolationMethod == BILINEAR)
		    {
			    if (x < 0.0 || y < 0.0 || x >= width - 1 || y >= height - 1)
				    return 0;
			    else
				    return (int)Math.Round(getInterpolatedPixel(x, y, pixels));
		    }
		    else if (interpolationMethod == BICUBIC)
		    {
			    int value = (int)(getBicubicInterpolatedPixel(x, y, this) + 0.5);
			    if (value < 0) value = 0;
			    if (value > 255) value = 255;
			    return value;
		    }
		    else
			    return getPixel((int)(x + 0.5), (int)(y + 0.5));
	    }

	    public override float getPixelValue(int x, int y)
	    {
		    if (x >= 0 && x < width && y >= 0 && y < height)
		    {
			    if (cTable == null)
				    return pixels[y * width + x] & 0xff;
			    else
				    return cTable[pixels[y * width + x] & 0xff];
		    }
		    else
			    return float.NaN;
	    }

	    /** Sets the foreground drawing color. */
	    public override void setColor(Color color)
	    {
		    drawingColor = color;
		    fgColor = getBestIndex(color);
		    _fillValueSet = true;
	    }

	    /** Sets the background fill/draw color. */
	    public void setBackgroundColor(Color color)
	    {
		    setBackgroundValue(getBestIndex(color));
	    }

	    /** Sets the default fill/draw value, where 0<=value<=255. */
	    public override void setValue(double value)
	    {
		    fgColor = (int)value;
		    if (fgColor < 0) fgColor = 0;
		    if (fgColor > 255) fgColor = 255;
		    _fillValueSet = true;
	    }

	    /** Returns the foreground fill/draw value. */
	    public double getForegroundValue()
	    {
		    return fgColor;
	    }

	    /** Sets the background fill value, where 0<=value<=255. */
	    public override void setBackgroundValue(double value)
	    {
		    bgColor = (int)value;
		    if (bgColor < 0) bgColor = 0;
		    if (bgColor > 255) bgColor = 255;
		    bgColorSet = true;
	    }

	    /** Returns the background fill value. */
	    public override double getBackgroundValue()
	    {
		    return bgColor;
	    }

	    /** Stores the specified real value at (x,y). Does
		    nothing if (x,y) is outside the image boundary.
		    Values outside the range 0-255 are clamped. */
	    public override void putPixelValue(int x, int y, double value)
	    {
		    if (x >= 0 && x < width && y >= 0 && y < height)
		    {
			    if (value > 255.0)
				    value = 255.0;
			    else if (value < 0.0)
				    value = 0.0;
			    pixels[y * width + x] = (byte)(value + 0.5);
		    }
	    }

	    /** Stores the specified value at (x,y). Does
		    nothing if (x,y) is outside the image boundary.
		    Values outside the range 0-255 are clamped. */
	    public override void putPixel(int x, int y, int value)
	    {
		    if (x >= 0 && x < width && y >= 0 && y < height)
		    {
			    if (value > 255) value = 255;
			    if (value < 0) value = 0;
			    pixels[y * width + x] = (byte)value;
		    }
	    }

	    /** Draws a pixel in the current foreground color. */
	    public override void drawPixel(int x, int y)
	    {
		    if (x >= clipXMin && x <= clipXMax && y >= clipYMin && y <= clipYMax)
			    pixels[y * width + x] = (byte)fgColor;
	    }

	    /**	Returns a reference to the byte array containing this image's
		    pixel data. To avoid sign extension, the pixel values must be
		    accessed using a mask (e.g. int i = pixels[j]&0xff). */
	    public override object getPixels()
	    {
		    return (object)pixels;
	    }

	    /** Returns a copy of the pixel data. Or returns a reference to the
		    snapshot buffer if it is not null and 'snapshotCopyMode' is true.
		    @see ImageProcessor#snapshot
		    @see ImageProcessor#setSnapshotCopyMode
	    */
	    public override object getPixelsCopy()
	    {
		    if (snapshotPixels != null && snapshotCopyMode)
		    {
			    snapshotCopyMode = false;
			    return snapshotPixels;
		    }
		    else
		    {
			    byte[] pixels2 = new byte[width * height];
			    Array.Copy(pixels, 0, pixels2, 0, width * height);
			    return pixels2;
		    }
	    }

	    public override void setPixels(object pixels)
	    {
		    if (pixels != null && this.pixels != null && (((byte[])pixels).Length != this.pixels.Length))
			    throw new ArgumentException("");
		    this.pixels = (byte[])pixels;
		    resetPixels(pixels);
		    if (pixels == null) snapshotPixels = null;
		    raster = null;
		    image = null;
	    }

	    /** Returns the smallest displayed pixel value. */
	    public override double getMin()
	    {
		    return min;
	    }

	    /** Returns the largest displayed pixel value. */
	    public override double getMax()
	    {
		    return max;
	    }

	    /** Maps the entries in this image's LUT from min-max to 0-255. */
	    public override void setMinAndMax(double min, double max)
	    {
		    if (max < min)
			    return;
		    this.min = (int)Math.Round(min);
		    this.max = (int)Math.Round(max);
		    if (rLUT1 == null)
		    {
			    if (cm == null)
				    makeDefaultColorModel();
			    baseCM = cm;
			    IndexColorModel m = (IndexColorModel)cm;
			    rLUT1 = new byte[256]; gLUT1 = new byte[256]; bLUT1 = new byte[256];
			    m.getReds(rLUT1); m.getGreens(gLUT1); m.getBlues(bLUT1);
			    rLUT2 = new byte[256]; gLUT2 = new byte[256]; bLUT2 = new byte[256];
		    }
		    if (rLUT2 == null)
			    return;
		    int index;
		    for (int i = 0; i < 256; i++)
		    {
			    if (i < min)
			    {
				    rLUT2[i] = rLUT1[0];
				    gLUT2[i] = gLUT1[0];
				    bLUT2[i] = bLUT1[0];
			    }
			    else if (i > max)
			    {
				    rLUT2[i] = rLUT1[255];
				    gLUT2[i] = gLUT1[255];
				    bLUT2[i] = bLUT1[255];
			    }
			    else
			    {
				    index = i - this.min;
				    index = (int)(256.0 * index / (max - min));
				    if (index < 0)
					    index = 0;
				    if (index > 255)
					    index = 255;
				    rLUT2[i] = rLUT1[index];
				    gLUT2[i] = gLUT1[index];
				    bLUT2[i] = bLUT1[index];
			    }
		    }
		    cm = new IndexColorModel(8, 256, rLUT2, gLUT2, bLUT2);
		    minThreshold = NO_THRESHOLD;
	    }

	    /** Resets this image's LUT. */
	    public override void resetMinAndMax()
	    {
		    setMinAndMax(0, 255);
	    }

	    public override void setThreshold(double minThreshold, double maxThreshold, int lutUpdate)
	    {
		    base.setThreshold(minThreshold, maxThreshold, lutUpdate);
		    if (this.minThreshold < 0.0 && this.minThreshold != NO_THRESHOLD)
			    this.minThreshold = 0.0;
		    if (this.maxThreshold > 255.0)
			    this.maxThreshold = 255.0;
	    }

	    /** Copies the image contained in 'ip' to (xloc, yloc) using one of
		    the transfer modes defined in the Blitter interface. */
	    public override void copyBits(ImageProcessor ip, int xloc, int yloc, int mode)
	    {
		    bool temporaryFloat = ip.getBitDepth() == 32 && (mode == Blitter.MULTIPLY || mode == Blitter.DIVIDE);
            if (temporaryFloat)
            {
             FloatProcessor ipFloat = this.convertToFloatProcessor();
             new FloatBlitter(ipFloat).copyBits(ip, xloc, yloc, mode);
             setPixels(1, ipFloat);
            }
            else
            {
             ip = ip.convertToByte(true);
             new ByteBlitter(this).copyBits(ip, xloc, yloc, mode);
            }
        }

	    /* Filters start here */

	    public override void applyTable(int[] lut)
	    {
		    int lineStart, lineEnd;
		    for (int y = roiY; y < (roiY + roiHeight); y++)
		    {
			    lineStart = y * width + roiX;
			    lineEnd = lineStart + roiWidth;
			    for (int i = lineEnd; --i >= lineStart;)
				    pixels[i] = (byte)lut[pixels[i] & 0xff];
		    }
	    }

	    public override void convolve3x3(int[] kernel)
	    {
		    int v1, v2, v3;    //input pixel values around the current pixel
		    int v4, v5, v6;
		    int v7, v8, v9;
		    int scale = 0;
		    int k1 = kernel[0], k2 = kernel[1], k3 = kernel[2],
		    k4 = kernel[3], k5 = kernel[4], k6 = kernel[5],
		    k7 = kernel[6], k8 = kernel[7], k9 = kernel[8];
		    for (int i = 0; i < kernel.Length; i++)
			    scale += kernel[i];
		    if (scale == 0) scale = 1;
		    byte[] pixels2 = (byte[])getPixelsCopy();
		    int xEnd = roiX + roiWidth;
		    int yEnd = roiY + roiHeight;
		    for (int y = roiY; y < yEnd; y++)
		    {
			    int p = roiX + y * width;            //points to current pixel
			    int p6 = p - (roiX > 0 ? 1 : 0);      //will point to v6, currently lower
			    int p3 = p6 - (y > 0 ? width : 0);    //will point to v3, currently lower
			    int p9 = p6 + (y < height - 1 ? width : 0); // ...  to v9, currently lower
			    v2 = pixels2[p3] & 0xff;
			    v5 = pixels2[p6] & 0xff;
			    v8 = pixels2[p9] & 0xff;
			    if (roiX > 0) { p3++; p6++; p9++; }
			    v3 = pixels2[p3] & 0xff;
			    v6 = pixels2[p6] & 0xff;
			    v9 = pixels2[p9] & 0xff;
			    for (int x = roiX; x < xEnd; x++, p++)
			    {
				    if (x < width - 1) { p3++; p6++; p9++; }
				    v1 = v2; v2 = v3;
				    v3 = pixels2[p3] & 0xff;
				    v4 = v5; v5 = v6;
				    v6 = pixels2[p6] & 0xff;
				    v7 = v8; v8 = v9;
				    v9 = pixels2[p9] & 0xff;
				    int sum = k1 * v1 + k2 * v2 + k3 * v3
						    + k4 * v4 + k5 * v5 + k6 * v6
						    + k7 * v7 + k8 * v8 + k9 * v9;
				    sum = (sum + scale / 2) / scale;   //add scale/2 to round
				    if (sum > 255) sum = 255;
				    if (sum < 0) sum = 0;
				    pixels[p] = (byte)sum;
			    }
		    }
	    }

	    /** Filters using a 3x3 neighborhood. The p1, p2, etc variables, which
		    contain the values of the pixels in the neighborhood, are arranged
		    as follows:
		    <pre>
		        p1 p2 p3
		        p4 p5 p6
		        p7 p8 p9
		    </pre>
	    */
	    public override void filter(int type)
	    {
		    int p1, p2, p3, p4, p5, p6, p7, p8, p9;
		    byte[] pixels2 = (byte[])getPixelsCopy();
		    if (width == 1)
		    {
			    filterEdge(type, pixels2, roiHeight, roiX, roiY, 0, 1);
			    return;
		    }
		    int offset, sum1, sum2 = 0, sum = 0;
		    int[] values = new int[10];
		    if (type == MEDIAN_FILTER) values = new int[10];
		    int rowOffset = width;
		    int count;
		    int binaryForeground = 255 - binaryBackground;
		    for (int y = yMin; y <= yMax; y++)
		    {
			    offset = xMin + y * width;
			    p2 = pixels2[offset - rowOffset - 1] & 0xff;
			    p3 = pixels2[offset - rowOffset] & 0xff;
			    p5 = pixels2[offset - 1] & 0xff;
			    p6 = pixels2[offset] & 0xff;
			    p8 = pixels2[offset + rowOffset - 1] & 0xff;
			    p9 = pixels2[offset + rowOffset] & 0xff;

			    for (int x = xMin; x <= xMax; x++)
			    {
				    p1 = p2; p2 = p3;
				    p3 = pixels2[offset - rowOffset + 1] & 0xff;
				    p4 = p5; p5 = p6;
				    p6 = pixels2[offset + 1] & 0xff;
				    p7 = p8; p8 = p9;
				    p9 = pixels2[offset + rowOffset + 1] & 0xff;

				    switch (type)
				    {
					    case BLUR_MORE:
						    sum = (p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + 4) / 9;
						    break;
					    case FIND_EDGES: // 3x3 Sobel filter
						    sum1 = p1 + 2 * p2 + p3 - p7 - 2 * p8 - p9;
						    sum2 = p1 + 2 * p4 + p7 - p3 - 2 * p6 - p9;
						    sum = (int)Math.Sqrt(sum1 * sum1 + sum2 * sum2);
						    if (sum > 255) sum = 255;
						    break;
					    case MEDIAN_FILTER:
						    values[1] = p1; values[2] = p2; values[3] = p3; values[4] = p4; values[5] = p5;
						    values[6] = p6; values[7] = p7; values[8] = p8; values[9] = p9;
						    sum = findMedian(values);
						    break;
					    case MIN:
						    sum = p5;
						    if (p1 < sum) sum = p1;
						    if (p2 < sum) sum = p2;
						    if (p3 < sum) sum = p3;
						    if (p4 < sum) sum = p4;
						    if (p6 < sum) sum = p6;
						    if (p7 < sum) sum = p7;
						    if (p8 < sum) sum = p8;
						    if (p9 < sum) sum = p9;
						    break;
					    case MAX:
						    sum = p5;
						    if (p1 > sum) sum = p1;
						    if (p2 > sum) sum = p2;
						    if (p3 > sum) sum = p3;
						    if (p4 > sum) sum = p4;
						    if (p6 > sum) sum = p6;
						    if (p7 > sum) sum = p7;
						    if (p8 > sum) sum = p8;
						    if (p9 > sum) sum = p9;
						    break;
					    case ERODE:
						    if (p5 == binaryBackground)
							    sum = binaryBackground;
						    else
						    {
							    count = 0;
							    if (p1 == binaryBackground) count++;
							    if (p2 == binaryBackground) count++;
							    if (p3 == binaryBackground) count++;
							    if (p4 == binaryBackground) count++;
							    if (p6 == binaryBackground) count++;
							    if (p7 == binaryBackground) count++;
							    if (p8 == binaryBackground) count++;
							    if (p9 == binaryBackground) count++;
							    if (count >= binaryCount)
								    sum = binaryBackground;
							    else
								    sum = binaryForeground;
						    }
						    break;
					    case DILATE:
						    if (p5 == binaryForeground)
							    sum = binaryForeground;
						    else
						    {
							    count = 0;
							    if (p1 == binaryForeground) count++;
							    if (p2 == binaryForeground) count++;
							    if (p3 == binaryForeground) count++;
							    if (p4 == binaryForeground) count++;
							    if (p6 == binaryForeground) count++;
							    if (p7 == binaryForeground) count++;
							    if (p8 == binaryForeground) count++;
							    if (p9 == binaryForeground) count++;
							    if (count >= binaryCount)
								    sum = binaryForeground;
							    else
								    sum = binaryBackground;
						    }
						    break;
				    }

				    pixels[offset++] = (byte)sum;
			    }
		    }
		    if (xMin == 1) filterEdge(type, pixels2, roiHeight, roiX, roiY, 0, 1);
		    if (yMin == 1) filterEdge(type, pixels2, roiWidth, roiX, roiY, 1, 0);
		    if (xMax == width - 2) filterEdge(type, pixels2, roiHeight, width - 1, roiY, 0, 1);
		    if (yMax == height - 2) filterEdge(type, pixels2, roiWidth, roiX, height - 1, 1, 0);
	    }

	    void filterEdge(int type, byte[] pixels2, int n, int x, int y, int xinc, int yinc)
	    {
		    int p1, p2, p3, p4, p5, p6, p7, p8, p9;
		    int sum = 0, sum1, sum2;
		    int count;
		    int binaryForeground = 255 - binaryBackground;
		    int bg = binaryBackground;
		    int fg = binaryForeground;

		    for (int i = 0; i < n; i++)
		    {
			    if ((!Prefs.padEdges && type == ERODE) || type == DILATE)
			    {
				    p1 = getEdgePixel0(pixels2, bg, x - 1, y - 1); p2 = getEdgePixel0(pixels2, bg, x, y - 1); p3 = getEdgePixel0(pixels2, bg, x + 1, y - 1);
				    p4 = getEdgePixel0(pixels2, bg, x - 1, y); p5 = getEdgePixel0(pixels2, bg, x, y); p6 = getEdgePixel0(pixels2, bg, x + 1, y);
				    p7 = getEdgePixel0(pixels2, bg, x - 1, y + 1); p8 = getEdgePixel0(pixels2, bg, x, y + 1); p9 = getEdgePixel0(pixels2, bg, x + 1, y + 1);
			    }
			    else if (Prefs.padEdges && type == ERODE)
			    {
				    p1 = getEdgePixel1(pixels2, fg, x - 1, y - 1); p2 = getEdgePixel1(pixels2, fg, x, y - 1); p3 = getEdgePixel1(pixels2, fg, x + 1, y - 1);
				    p4 = getEdgePixel1(pixels2, fg, x - 1, y); p5 = getEdgePixel1(pixels2, fg, x, y); p6 = getEdgePixel1(pixels2, fg, x + 1, y);
				    p7 = getEdgePixel1(pixels2, fg, x - 1, y + 1); p8 = getEdgePixel1(pixels2, fg, x, y + 1); p9 = getEdgePixel1(pixels2, fg, x + 1, y + 1);
			    }
			    else
			    {
				    p1 = getEdgePixel(pixels2, x - 1, y - 1); p2 = getEdgePixel(pixels2, x, y - 1); p3 = getEdgePixel(pixels2, x + 1, y - 1);
				    p4 = getEdgePixel(pixels2, x - 1, y); p5 = getEdgePixel(pixels2, x, y); p6 = getEdgePixel(pixels2, x + 1, y);
				    p7 = getEdgePixel(pixels2, x - 1, y + 1); p8 = getEdgePixel(pixels2, x, y + 1); p9 = getEdgePixel(pixels2, x + 1, y + 1);
			    }
			    switch (type)
			    {
				    case BLUR_MORE:
					    sum = (p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + 4) / 9;
					    break;
				    case FIND_EDGES: // 3x3 Sobel filter
					    sum1 = p1 + 2 * p2 + p3 - p7 - 2 * p8 - p9;
					    sum2 = p1 + 2 * p4 + p7 - p3 - 2 * p6 - p9;
					    sum = (int)Math.Sqrt(sum1 * sum1 + sum2 * sum2);
					    if (sum > 255) sum = 255;
					    break;
				    case MIN:
					    sum = p5;
					    if (p1 < sum) sum = p1;
					    if (p2 < sum) sum = p2;
					    if (p3 < sum) sum = p3;
					    if (p4 < sum) sum = p4;
					    if (p6 < sum) sum = p6;
					    if (p7 < sum) sum = p7;
					    if (p8 < sum) sum = p8;
					    if (p9 < sum) sum = p9;
					    break;
				    case MAX:
					    sum = p5;
					    if (p1 > sum) sum = p1;
					    if (p2 > sum) sum = p2;
					    if (p3 > sum) sum = p3;
					    if (p4 > sum) sum = p4;
					    if (p6 > sum) sum = p6;
					    if (p7 > sum) sum = p7;
					    if (p8 > sum) sum = p8;
					    if (p9 > sum) sum = p9;
					    break;
				    case ERODE:
					    if (p5 == binaryBackground)
						    sum = binaryBackground;
					    else
					    {
						    count = 0;
						    if (p1 == binaryBackground) count++;
						    if (p2 == binaryBackground) count++;
						    if (p3 == binaryBackground) count++;
						    if (p4 == binaryBackground) count++;
						    if (p6 == binaryBackground) count++;
						    if (p7 == binaryBackground) count++;
						    if (p8 == binaryBackground) count++;
						    if (p9 == binaryBackground) count++;
						    if (count >= binaryCount)
							    sum = binaryBackground;
						    else
							    sum = binaryForeground;
					    }
					    break;
				    case DILATE:
					    if (p5 == binaryForeground)
						    sum = binaryForeground;
					    else
					    {
						    count = 0;
						    if (p1 == binaryForeground) count++;
						    if (p2 == binaryForeground) count++;
						    if (p3 == binaryForeground) count++;
						    if (p4 == binaryForeground) count++;
						    if (p6 == binaryForeground) count++;
						    if (p7 == binaryForeground) count++;
						    if (p8 == binaryForeground) count++;
						    if (p9 == binaryForeground) count++;
						    if (count >= binaryCount)
							    sum = binaryForeground;
						    else
							    sum = binaryBackground;
					    }
					    break;
			    }
			    pixels[x + y * width] = (byte)sum;
			    x += xinc; y += yinc;
		    }
	    }

	    int getEdgePixel(byte[] pixels2, int x, int y)
	    {
		    if (x <= 0) x = 0;
		    if (x >= width) x = width - 1;
		    if (y <= 0) y = 0;
		    if (y >= height) y = height - 1;
		    return pixels2[x + y * width] & 255;
	    }

	    int getEdgePixel1(byte[] pixels2, int foreground, int x, int y)
	    {
		    if (x < 0 || x > width - 1 || y < 0 || y > height - 1)
			    return foreground;
		    else
			    return pixels2[x + y * width] & 255;
	    }

	    int getEdgePixel0(byte[] pixels2, int background, int x, int y)
	    {
		    if (x < 0 || x > width - 1 || y < 0 || y > height - 1)
			    return background;
		    else
			    return pixels2[x + y * width] & 255;
	    }

	    public void erode()
	    {
		    if (isInvertedLut())
			    filter(MIN);
		    else
			    filter(MAX);
	    }

	    public void dilate()
	    {
		    if (isInvertedLut())
			    filter(MAX);
		    else
			    filter(MIN);
	    }

	    public void erode(int count, int background)
	    {
		    binaryCount = count;
		    binaryBackground = background;
		    filter(ERODE);
	    }

	    public void dilate(int count, int background)
	    {
		    binaryCount = count;
		    binaryBackground = background;
		    filter(DILATE);
	    }

	    public void outline()
        {
            throw new NotImplementedException();
            // new BinaryProcessor(this).outline();
        }

	    /** Converts black objects in a binary image to single pixel skeletons. */
	    public void skeletonize()
	    {
            throw new NotImplementedException();
			// new BinaryProcessor(this).skeletonize();
	    }

	    /** Converts objects with pixel values of 'forground' (255 or 0) 
		    in a binary imager to single pixel skeletons.
	    */
	    public void skeletonize(int foreground)
	    {
            throw new NotImplementedException();
			// new BinaryProcessor(this).skeletonize(foreground);
	    }

	    private int findMedian(int[] values)
	    {
		    //Finds the 5th largest of 9 values
		    for (int i = 1; i <= 4; i++)
		    {
			    int max = 0;
			    int mj = 1;
			    for (int j = 1; j <= 9; j++)
				    if (values[j] > max)
				    {
					    max = values[j];
					    mj = j;
				    }
			    values[mj] = 0;
		    }

            {
                int max = 0;
                for (int j = 1; j <= 9; j++)
                    if (values[j] > max)
                        max = values[j];
                return max;
            }
        }

	    public override void medianFilter()
	    {
		    filter(MEDIAN_FILTER);
	    }


	    /** Adds pseudorandom, Gaussian ("normally") distributed values, with
    	    mean 0.0 and the specified standard deviation, to this image or ROI. */
	    public override void noise(double standardDeviation)
	    {
		    if (rnd == null)
			    rnd = new Random();
		    if (!double.IsNaN(seed))
			    rnd = new Random((int)seed);
		    seed = double.NaN;
		    int v, ran;
		    bool inRange;
		    for (int y = roiY; y < (roiY + roiHeight); y++)
		    {
			    int i = y * width + roiX;
			    for (int x = roiX; x < (roiX + roiWidth); x++)
			    {
				    inRange = false;
				    do
				    {
						//todo:
					    // ran = (int)Math.Round(rnd.nextGaussian() * standardDeviation);
					    ran = (int)Math.Round(rnd.NextDouble() * standardDeviation);
					    v = (pixels[i] & 0xff) + ran;
					    inRange = v >= 0 && v <= 255;
					    if (inRange) pixels[i] = (byte)v;
				    } while (!inRange);
				    i++;
			    }
		    }
	    }


	    /** Scales the image or selection using the specified scale factors.
		    @see ImageProcessor#setInterpolate
	    */
	    public override void scale(double xScale, double yScale)
	    {
		    double xCenter = roiX + roiWidth / 2.0;
		    double yCenter = roiY + roiHeight / 2.0;
		    int xmin, xmax, ymin, ymax;
		    if (!bgColorSet && isInvertedLut()) bgColor = 0;

		    if ((xScale > 1.0) && (yScale > 1.0))
		    {
			    //expand roi
			    xmin = (int)(xCenter - (xCenter - roiX) * xScale);
			    if (xmin < 0) xmin = 0;
			    xmax = xmin + (int)(roiWidth * xScale) - 1;
			    if (xmax >= width) xmax = width - 1;
			    ymin = (int)(yCenter - (yCenter - roiY) * yScale);
			    if (ymin < 0) ymin = 0;
			    ymax = ymin + (int)(roiHeight * yScale) - 1;
			    if (ymax >= height) ymax = height - 1;
		    }
		    else
		    {
			    xmin = roiX;
			    xmax = roiX + roiWidth - 1;
			    ymin = roiY;
			    ymax = roiY + roiHeight - 1;
		    }
		    byte[] pixels2 = (byte[])getPixelsCopy();
		    ImageProcessor ip2 = null;
		    if (interpolationMethod == BICUBIC)
		    {
			    ip2 = new ByteProcessor(getWidth(), getHeight(), pixels2, null);
			    ip2.setBackgroundValue(getBackgroundValue());
		    }
		    bool checkCoordinates = (xScale < 1.0) || (yScale < 1.0);
		    int index1, index2, xsi, ysi;
		    double ys, xs;
		    if (interpolationMethod == BICUBIC)
		    {
			    for (int y = ymin; y <= ymax; y++)
			    {
				    ys = (y - yCenter) / yScale + yCenter;
				    index1 = y * width + xmin;
				    index2 = width * (int)ys;
				    for (int x = xmin; x <= xmax; x++)
				    {
					    xs = (x - xCenter) / xScale + xCenter;
					    int value = (int)(getBicubicInterpolatedPixel(xs, ys, ip2) + 0.5);
					    if (value < 0) value = 0;
					    if (value > 255) value = 255;
					    pixels[index1++] = (byte)value;
				    }
			    }
		    }
		    else
		    {
			    double xlimit = width - 1.0, xlimit2 = width - 1.001;
			    double ylimit = height - 1.0, ylimit2 = height - 1.001;
			    for (int y = ymin; y <= ymax; y++)
			    {
				    ys = (y - yCenter) / yScale + yCenter;
				    ysi = (int)ys;
				    if (ys < 0.0) ys = 0.0;
				    if (ys >= ylimit) ys = ylimit2;
				    index1 = y * width + xmin;
				    index2 = width * (int)ys;
				    for (int x = xmin; x <= xmax; x++)
				    {
					    xs = (x - xCenter) / xScale + xCenter;
					    xsi = (int)xs;
					    if (checkCoordinates && ((xsi < xmin) || (xsi > xmax) || (ysi < ymin) || (ysi > ymax)))
						    pixels[index1++] = (byte)bgColor;
					    else
					    {
						    if (interpolationMethod == BILINEAR)
						    {
							    if (xs < 0.0) xs = 0.0;
							    if (xs >= xlimit) xs = xlimit2;
							    pixels[index1++] = (byte)((int)(getInterpolatedPixel(xs, ys, pixels2) + 0.5) & 255);
						    }
						    else
							    pixels[index1++] = pixels2[index2 + xsi];
					    }
				    }
			    }
		    }
	    }

	    /** Uses bilinear interpolation to find the pixel value at real coordinates (x,y). */
	    private double getInterpolatedPixel(double x, double y, byte[] pixels)
	    {
		    int xbase = (int)x;
		    int ybase = (int)y;
		    double xFraction = x - xbase;
		    double yFraction = y - ybase;
		    int offset = ybase * width + xbase;
		    int lowerLeft = pixels[offset] & 255;
		    int lowerRight = pixels[offset + 1] & 255;
		    int upperRight = pixels[offset + width + 1] & 255;
		    int upperLeft = pixels[offset + width] & 255;
		    double upperAverage = upperLeft + xFraction * (upperRight - upperLeft);
		    double lowerAverage = lowerLeft + xFraction * (lowerRight - lowerLeft);
		    return lowerAverage + yFraction * (upperAverage - lowerAverage);
	    }

	    /** Creates a new ByteProcessor containing a scaled copy of this image or selection.
		    @see ij.process.ImageProcessor#setInterpolate
	    */
	    public override ImageProcessor resize(int dstWidth, int dstHeight)
	    {
		    if (roiWidth == dstWidth && roiHeight == dstHeight)
			    return crop();
		    if ((width == 1 || height == 1) && interpolationMethod != NONE)
			    return resizeLinearly(dstWidth, dstHeight);
		    double srcCenterX = roiX + roiWidth / 2.0;
		    double srcCenterY = roiY + roiHeight / 2.0;
		    double dstCenterX = dstWidth / 2.0;
		    double dstCenterY = dstHeight / 2.0;
		    double xScale = (double)dstWidth / roiWidth;
		    double yScale = (double)dstHeight / roiHeight;
		    if (interpolationMethod != NONE)
		    {
			    if (dstWidth != width) dstCenterX += xScale / 4.0;
			    if (dstHeight != height) dstCenterY += yScale / 4.0;
		    }
		    ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
		    byte[] pixels2 = (byte[])ip2.getPixels();
		    int inc = getProgressIncrement(dstWidth, dstHeight);
		    double xs, ys;
		    int index1, index2;
		    if (interpolationMethod == BICUBIC)
		    {
			    for (int y = 0; y <= dstHeight - 1; y++)
			    {
				    if (inc != 0 && y % inc == 0) showProgress((double)y / dstHeight);
				    ys = (y - dstCenterY) / yScale + srcCenterY;
				    index1 = width * (int)ys;
				    index2 = y * dstWidth;
				    for (int x = 0; x <= dstWidth - 1; x++)
				    {
					    xs = (x - dstCenterX) / xScale + srcCenterX;
					    int value = (int)(getBicubicInterpolatedPixel(xs, ys, this) + 0.5);
					    if (value < 0) value = 0;
					    if (value > 255) value = 255;
					    pixels2[index2++] = (byte)value;
				    }
			    }
		    }
		    else
		    {
			    double xlimit = width - 1.0, xlimit2 = width - 1.001;
			    double ylimit = height - 1.0, ylimit2 = height - 1.001;
			    for (int y = 0; y <= dstHeight - 1; y++)
			    {
				    if (inc != 0 && y % inc == 0) showProgress((double)y / dstHeight);
				    ys = (y - dstCenterY) / yScale + srcCenterY;
				    if (interpolationMethod == BILINEAR)
				    {
					    if (ys < 0.0) ys = 0.0;
					    if (ys >= ylimit) ys = ylimit2;
				    }
				    index1 = width * (int)ys;
				    index2 = y * dstWidth;
				    for (int x = 0; x <= dstWidth - 1; x++)
				    {
					    xs = (x - dstCenterX) / xScale + srcCenterX;
					    if (interpolationMethod == BILINEAR)
					    {
						    if (xs < 0.0) xs = 0.0;
						    if (xs >= xlimit) xs = xlimit2;
						    pixels2[index2++] = (byte)((int)(getInterpolatedPixel(xs, ys, pixels) + 0.5) & 255);
					    }
					    else
						    pixels2[index2++] = pixels[index1 + (int)xs];
				    }
			    }
		    }
		    if (inc != 0) showProgress(1.0);
		    return ip2;
	    }

	    /** Rotates the image or ROI 'angle' degrees clockwise.
		    @see ImageProcessor#setInterpolationMethod
	    */
	    public void rotate(double angle)
	    {
		    if (angle % 360 == 0)
			    return;
		    byte[] pixels2 = (byte[])getPixelsCopy();
		    ImageProcessor ip2 = null;
		    if (interpolationMethod == BICUBIC)
		    {
			    ip2 = new ByteProcessor(getWidth(), getHeight(), pixels2, null);
			    ip2.setBackgroundValue(getBackgroundValue());
		    }
		    double centerX = roiX + (roiWidth - 1) / 2.0;
		    double centerY = roiY + (roiHeight - 1) / 2.0;
		    int xMax = roiX + this.roiWidth - 1;
		    if (!bgColorSet && isInvertedLut()) bgColor = 0;

		    double angleRadians = -angle / (180.0 / Math.PI);
		    double ca = Math.Cos(angleRadians);
		    double sa = Math.Sin(angleRadians);
		    double tmp1 = centerY * sa - centerX * ca;
		    double tmp2 = -centerX * sa - centerY * ca;
		    double tmp3, tmp4, xs, ys;
		    int index, ixs, iys;
		    double dwidth = width, dheight = height;
		    double xlimit = width - 1.0, xlimit2 = width - 1.001;
		    double ylimit = height - 1.0, ylimit2 = height - 1.001;

		    if (interpolationMethod == BICUBIC)
		    {
			    for (int y = roiY; y < (roiY + roiHeight); y++)
			    {
				    index = y * width + roiX;
				    tmp3 = tmp1 - y * sa + centerX;
				    tmp4 = tmp2 + y * ca + centerY;
				    for (int x = roiX; x <= xMax; x++)
				    {
					    xs = x * ca + tmp3;
					    ys = x * sa + tmp4;
					    int value = (int)(getBicubicInterpolatedPixel(xs, ys, ip2) + 0.5);
					    if (value < 0) value = 0;
					    if (value > 255) value = 255;
					    pixels[index++] = (byte)value;
				    }
			    }
		    }
		    else
		    {
			    for (int y = roiY; y < (roiY + roiHeight); y++)
			    {
				    index = y * width + roiX;
				    tmp3 = tmp1 - y * sa + centerX;
				    tmp4 = tmp2 + y * ca + centerY;
				    for (int x = roiX; x <= xMax; x++)
				    {
					    xs = x * ca + tmp3;
					    ys = x * sa + tmp4;
					    if ((xs >= -0.01) && (xs < dwidth) && (ys >= -0.01) && (ys < dheight))
					    {
						    if (interpolationMethod == BILINEAR)
						    {
							    if (xs < 0.0) xs = 0.0;
							    if (xs >= xlimit) xs = xlimit2;
							    if (ys < 0.0) ys = 0.0;
							    if (ys >= ylimit) ys = ylimit2;
							    pixels[index++] = (byte)(getInterpolatedPixel(xs, ys, pixels2) + 0.5);
						    }
						    else
						    {
							    ixs = (int)(xs + 0.5);
							    iys = (int)(ys + 0.5);
							    if (ixs >= width) ixs = width - 1;
							    if (iys >= height) iys = height - 1;
							    pixels[index++] = pixels2[width * iys + ixs];
						    }
					    }
					    else
						    pixels[index++] = (byte)bgColor;
				    }
			    }
		    }
	    }

	    public override void flipVertical()
	    {
		    int index1, index2;
		    byte tmp;
		    for (int y = 0; y < roiHeight / 2; y++)
		    {
			    index1 = (roiY + y) * width + roiX;
			    index2 = (roiY + roiHeight - 1 - y) * width + roiX;
			    for (int i = 0; i < roiWidth; i++)
			    {
				    tmp = pixels[index1];
				    pixels[index1++] = pixels[index2];
				    pixels[index2++] = tmp;
			    }
		    }
	    }

	    public override int[] getHistogram()
	    {
		    if (mask != null)
			    return getHistogram(mask);
		    int[] histogram = new int[256];
		    for (int y = roiY; y < (roiY + roiHeight); y++)
		    {
			    int i = y * width + roiX;
			    for (int x = roiX; x < (roiX + roiWidth); x++)
			    {
				    int v = pixels[i++] & 0xff;
				    histogram[v]++;
			    }
		    }
		    return histogram;
	    }

	    public int[] getHistogram(ImageProcessor mask)
	    {
		    int rx = roiX, ry = roiY, rw = roiWidth, rh = roiHeight;
		    if (mask.getWidth() != rw || mask.getHeight() != rh)
			    throw new ArgumentException(maskSizeError(mask));
		    int v;
		    int[] histogram = new int[256];
		    byte[] mpixels = (byte[])mask.getPixels();
		    for (int y = ry, my = 0; y < (ry + rh); y++, my++)
		    {
			    int i = y * width + rx;
			    int mi = my * rw;
			    for (int x = rx; x < (rx + rw); x++)
			    {
				    if (mpixels[mi++] != 0)
				    {
					    v = pixels[i] & 0xff;
					    histogram[v]++;
				    }
				    i++;
			    }
		    }
		    return histogram;
	    }

	    /** Sets pixels less than or equal to level to 0 and all other pixels to 255. */
	    public override void threshold(int level)
	    {
		    for (int i = 0; i < width * height; i++)
		    {
			    if ((pixels[i] & 0xff) <= level)
				    pixels[i] = 0;
			    else
				    pixels[i] = (byte)255;
		    }
	    }

	    public void applyLut()
	    {
		    if (rLUT2 == null)
			    return;
		    if (isInvertedLut())
			    for (int i = 0; i < width * height; i++)
				    pixels[i] = (byte)(255 - rLUT2[pixels[i] & 0xff]);
		    else
			    for (int i = 0; i < width * height; i++)
				    pixels[i] = rLUT2[pixels[i] & 0xff];
		    setMinAndMax(0, 255);
	    }

	    /** Performs a convolution operation using the specified kernel. */
	    public override void convolve(float[] kernel, int kernelWidth, int kernelHeight)
        {
            throw new NotImplementedException();
            // ImageProcessor ip2 = convertToFloat();
            // ip2.setRoi(getRoi());
            // new Plugin.Filter.Convolver().convolve(ip2, kernel, kernelWidth, kernelHeight);
            // ip2 = ip2.convertToByte(false);
            // byte[] pixels2 = (byte[])ip2.getPixels();
            // System.arraycopy(pixels2, 0, pixels, 0, pixels.length);
        }

	    public FloatProcessor[] toFloatProcessors()
	    {
		    FloatProcessor[] fp = new FloatProcessor[1];
		    fp[0] = (FloatProcessor)convertToFloat();
		    return fp;
	    }

	    public void setFromFloatProcessors(FloatProcessor[] fp)
	    {
		    ImageProcessor ip2 = fp[0].convertToByte(false);
		    setPixels(ip2.getPixels());
	    }

	    public float[][] toFloatArrays()
	    {
		    float[][] a = new float[1][];
		    //ImageProcessor fp = crop();
		    ImageProcessor fp = convertToFloat();
		    a[0] = (float[])fp.getPixels();
		    return a;
	    }

	    public void setFromFloatArrays(float[][] arrays)
	    {
		    ImageProcessor ip2 = new FloatProcessor(roiWidth, roiHeight, arrays[0], null);
		    ip2 = ip2.convertToByte(false);
		    setPixels(ip2.getPixels());
		    //insert(ip2, roiX, roiY); 
	    }

	    /** Returns a FloatProcessor with the same image, no scaling or calibration
	    *  (pixel values 0 to 255).
	    *  The roi, mask, lut (ColorModel), threshold, min&max are
	    *  also set for the FloatProcessor
	    *  @param channelNumber   Ignored (needed for compatibility with ColorProcessor.toFloat)
	    *  @param fp              Here a FloatProcessor can be supplied, or null. The FloatProcessor
	    *                         is overwritten by this method (re-using its pixels array 
	    *                         improves performance).
	    *  @return A FloatProcessor with the converted image data
	    */
	    public override FloatProcessor toFloat(int channelNumber, FloatProcessor fp)
	    {
		    int size = width * height;
		    if (fp == null || fp.getWidth() != width || fp.getHeight() != height)
			    fp = new FloatProcessor(width, height, new float[size], cm);
		    float[] fPixels = (float[])fp.getPixels();
		    for (int i = 0; i < size; i++)
			    fPixels[i] = pixels[i] & 0xff;
		    fp.setRoi(getRoi());
		    fp.setMask(mask);
		    fp.setMinAndMax(min, max);
		    fp.setThreshold(minThreshold, maxThreshold, ImageProcessor.NO_LUT_UPDATE);
		    return fp;
	    }

	    /** Sets the pixels from a FloatProcessor, no scaling.
	    *  Also the min&max values are taken from the FloatProcessor.
	    *  @param channelNumber   Ignored (needed for compatibility with ColorProcessor.toFloat)
	    *  @param fp              The FloatProcessor where the image data are read from.
	    */
	    public override void setPixels(int channelNumber, FloatProcessor fp)
	    {
		    float[] fPixels = (float[])fp.getPixels();
		    float value;
		    int size = width * height;
		    for (int i = 0; i < size; i++)
		    {
			    value = fPixels[i] + 0.5f;
			    if (value < 0f) value = 0f;
			    if (value > 255f) value = 255f;
			    pixels[i] = (byte)value;
		    }
		    setMinAndMax(fp.getMin(), fp.getMax());
	    }

	    /** Returns 'true' if this is a binary image (8-bit-image with only 0 and 255). */
	    public bool isBinary()
	    {
		    for (int i = 0; i < width * height; i++)
		    {
			    if (pixels[i] != 0 && pixels[i] != (byte)255)
				    return false;
		    }
		    return true;
	    }

	    public override int getBitDepth()
	    {
		    return 8;
	    }

	    /** Returns a binary mask, or null if a threshold is not set. */
	    public override ByteProcessor createMask()
	    {
		    if (getMinThreshold() == NO_THRESHOLD)
			    return null;
		    int minThreshold = (int)getMinThreshold();
		    int maxThreshold = (int)getMaxThreshold();
		    ByteProcessor mask = new ByteProcessor(width, height);
		    byte[] mpixels = (byte[])mask.getPixels();
		    for (int i = 0; i < pixels.Length; i++)
		    {
			    int value = pixels[i] & 0xff;
			    if (value >= minThreshold && value <= maxThreshold)
				    mpixels[i] = (byte)255;
		    }
		    return mask;
	    }

        public override byte[] create8BitImage()
	    {
		    return pixels;
	    }

    }


}
